/*
 * @[H]:  Copyright (c) 2021 Phytium Information Technology, Inc. 
 * 
 *  SPDX-License-Identifier: Apache-2.0. 
 * 
 * @Date: 2021-09-01 14:42:33
 * @LastEditTime: 2021-09-04 23:14:09
 * @Description:  Description of file
 * @Modify History: 
 * * * Ver   Who        Date         Changes
 * * ----- ------     --------    --------------------------------------
 */


 /*****************************************************************************
 * atomic_shmem_demod.c - Shared memory atomic operation demo
 * This task will:
 *  1. Get the shared memory device I/O region.
 *  2. Get the IPI device I/O region.
 *  3. Register IPI interrupt handler.
 *  4. Wait for the APU to kick IPI to start the demo
 *  5. Once notification is received, start atomic add by
 *     1 for 5000 times over the shared memory
 *  6. Trigger IPI to notify the remote it has finished calculation.
 *  7. Clean up: Disable IPI interrupt, deregister the IPI interrupt handler.
 */
#include <metal/shmem.h>
#include <metal/atomic.h>
#include <metal/device.h>
#include <metal/io.h>
#include <sys/time.h>
#include <stdio.h>
#include "common.h"
#include "sys_init.h"
#include "interrupt.h"
#include "sdkconfig.h"
#include "fsleep.h"



#include "ft_debug.h"
#define LIBMETAL_ATSH_RPU_DEBUG_TAG "LIBMETAL_ATSH_RPU"
#define LIBMETAL_ATSH_RPU_DEBUG_I(format, ...) FT_DEBUG_PRINT_I(LIBMETAL_ATSH_RPU_DEBUG_TAG, format, ##__VA_ARGS__)
#define LIBMETAL_ATSH_RPU_DEBUG_W(format, ...) FT_DEBUG_PRINT_W(LIBMETAL_ATSH_RPU_DEBUG_TAG, format, ##__VA_ARGS__)
#define LIBMETAL_ATSH_RPU_DEBUG_E(format, ...) FT_DEBUG_PRINT_E(LIBMETAL_ATSH_RPU_DEBUG_TAG, format, ##__VA_ARGS__)


#define ATOMIC_INT_OFFSET 0x0 /* shared memory offset for atomic operation */
#define ITERATIONS 5000

static atomic_int remote_nkicked; /* is remote kicked, 0 - kicked,
				       1 - not-kicked */

static void ipi_irq_handler (s32 vect_id, void *priv)
{
	(void)vect_id;
    atomic_flag_clear(&remote_nkicked);
}

/**
 * @brief   atomic_add_shmemd() - Shared memory atomic operation demo
 *          This task will:
 *          * Wait for the remote to write to shared memory.
 *          * Once it receives the notification via polling, start atomic add by
 *            1 for 5000 times to first 32 bits of memory in the shared memory
 *            which is pointed to by shm_io.
 *          * Write to shared mem to notify the remote once it finishes
 *            calculation.
 *
 * @param[in] ipi_io - IPI metal i/o region
 * @param[in] shm_io - shared memory metal i/o region
 * @return - If setup failed, return the corresponding error number. Otherwise
 *          return 0 on success.
 */
int atomic_add_shmemd(struct metal_io_region *ipi_io,
		struct metal_io_region *shm_io)
{
	atomic_int *shm_int;
	int i;

	LIBMETAL_ATSH_RPU_DEBUG_I("Starting atomic add on shared memory demo.\n");
	shm_int = (atomic_int *)metal_io_virt(shm_io,
					ATOMIC_INT_OFFSET);

	/* Wait for notification from the remote to start the demo */
	wait_for_notified(&remote_nkicked);

	/* Do atomic add over the shared memory */
	for (i = 0; i < ITERATIONS; i++)
		atomic_fetch_add(shm_int, 1);

	/* Write to IPI trigger register to notify the remote it has finished
	 * the atomic operation. */

    IpiSend(CONFIG_IPI_IRQ_NUM,CONFIG_TARGET_CPU_ID) ;
    

	LIBMETAL_ATSH_RPU_DEBUG_I("Shared memory with atomics test finished\n");
	return 0;
}

int atomic_shmem_demod()
{
	struct metal_io_region *ipi_io = NULL, *shm_io = NULL;
	int ipi_irq;
	int ret = 0;

	LIBMETAL_ATSH_RPU_DEBUG_I("atomic operation over shared memory");

	/* Get shared memory device IO region */
	if (!shm_dev) {
		ret = -ENODEV;
		goto out;
	}
	shm_io = metal_device_io_region(shm_dev, 0);
	if (!shm_io) {
		LIBMETAL_ATSH_RPU_DEBUG_E("Failed to map io region for %s.\n", shm_dev->name);
		ret = -ENODEV;
		goto out;
	}


	/* Get the IPI IRQ from the opened IPI device */
    InterruptSetPriority(CONFIG_IPI_IRQ_NUM,CONFIG_IPI_IRQ_NUM_PRIORITY) ;
	IpiHandlerInstall(CONFIG_IPI_IRQ_NUM,ipi_irq_handler) ;

	/* initialize remote_nkicked */
	atomic_init(&remote_nkicked, 1);
	/* Enable IPI interrupt */
    InterruptUmask(CONFIG_IPI_IRQ_NUM) ;

	/* Run atomic operation demo */
	ret = atomic_add_shmemd(ipi_io, shm_io);

	/* disable IPI interrupt */
    InterruptMask(CONFIG_IPI_IRQ_NUM) ;
	/* unregister IPI irq handler */

out:
	return ret;

}

